#include "des.h"
//初始置换表
const static unsigned char ip_table[64] = {
        58 , 50 , 42 , 34 , 26 , 18 , 10 , 2 , 60 , 52 , 44 , 36 , 28 , 20 , 12 , 4 ,
        62 , 54 , 46 , 38 , 30 , 22 , 14 , 6 , 64 , 56 , 48 , 40 , 32 , 24 , 16 , 8 ,
        57 , 49 , 41 , 33 , 25 , 17 , 9 , 1 , 59 , 51 , 43 , 35 , 27 , 19 , 11 , 3 ,
        61 , 53 , 45 , 37 , 29 , 21 , 13 , 5 , 63 , 55 , 47 , 39 , 31 , 23 , 15 , 7
} ;

//扩展置换，将数据从32位扩展为48位
static const unsigned char expa_perm[48] = {
        32 , 1 , 2 , 3 , 4 , 5 , 4 , 5 , 6 , 7 , 8 , 9 , 8 , 9 , 10 , 11 ,
        12 , 13 , 12 , 13 , 14 , 15 , 16 , 17 , 16 , 17 , 18 , 19 , 20 , 21 , 20 , 21 ,
        22 , 23 , 24 , 25 , 24 , 25 , 26 , 27 , 28 , 29 , 28 , 29 , 30 , 31 , 32 , 1
} ;

//S盒子代替
const static unsigned char sbox[8][64]={
        {//S1盒子
                14 , 4 , 13 , 1 , 2 , 15 , 11 , 8 , 3 , 10 , 6 , 12 , 5 , 9 , 0 , 7 ,
                0 , 15 , 7 , 4 , 14 , 2 , 13 , 1 , 10 , 6 , 12 , 11 , 9 , 5 , 3 , 8 ,
                4 , 1 , 14 , 8 , 13 , 6 , 2 , 11 , 15 , 12 , 9 , 7 , 3 , 10 , 5 , 0 ,
                15 , 12 , 8 , 2 , 4 , 9 , 1 , 7 , 5 , 11 , 3 , 14 , 10 , 0 , 6 , 13
        } ,
        {//S2盒子
                15 , 1 , 8 , 14 , 6 , 11 , 3 , 4 , 9 , 7 , 2 , 13 , 12 , 0 , 5 , 10 ,
                3 , 13 , 4 , 7 , 15 , 2 , 8 , 14 , 12 , 0 , 1 , 10 , 6 , 9 , 11 , 5 ,
                0 , 14 , 7 , 11 , 10 , 4 , 13 , 1 , 5 , 8 , 12 , 6 , 9 , 3 , 2 , 15 ,
                13 , 8 , 10 , 1 , 3 , 15 , 4 , 2 , 11 , 6 , 7 , 12 , 0 , 5 , 14 , 9
        } ,
        {//S3盒子
                10 , 0 , 9 , 14 , 6 , 3 , 15 , 5 , 1 , 13 , 12 , 7 , 11 , 4 , 2 , 8 ,
                13 , 7 , 0 , 9 , 3 , 4 , 6 , 10 , 2 , 8 , 5 , 14 , 12 , 11 , 15 , 1 ,
                13 , 6 , 4 , 9 , 8 , 15 , 3 , 0 , 11 , 1 , 2 , 12 , 5 , 10 , 14 , 7 ,
                1 , 10 , 13 , 0 , 6 , 9 , 8 , 7 , 4 , 15 , 14 , 3 , 11 , 5 , 2 , 12
        } ,
        {//S4盒子
                7 , 13 , 14 , 3 , 0 , 6 , 9 , 10 , 1 , 2 , 8 , 5 , 11 , 12 , 4 , 15 ,
                13 , 8 , 11 , 5 , 6 , 15 , 0 , 3 , 4 , 7 , 2 , 12 , 1 , 10 , 14 , 9 ,
                10 , 6 , 9 , 0 , 12 , 11 , 7 , 13 , 15 , 1 , 3 , 14 , 5 , 2 , 8 , 4 ,
                3 , 15 , 0 , 6 , 10 , 1 , 13 , 8 , 9 , 4 , 5 , 11 , 12 , 7 , 2 , 14
        } ,
        {//S5盒子
                2 , 12 , 4 , 1 , 7 , 10 , 11 , 6 , 8 , 5 , 3 , 15 , 13 , 0 , 14 , 9 ,
                14 , 11 , 2 , 12 , 4 , 7 , 13 , 1 , 5 , 0 , 15 , 10 , 3 , 9 , 8 , 6 ,
                4 , 2 , 1 , 11 , 10 , 13 , 7 , 8 , 15 , 9 , 12 , 5 , 6 , 3 , 0 , 14 ,
                11 , 8 , 12 , 7 , 1 , 14 , 2 , 13 , 6 , 15 , 0 , 9 , 10 , 4 , 5 , 3
        } ,
        {//S6盒子
                12 , 1 , 10 , 15 , 9 , 2 , 6 , 8 , 0 , 13 , 3 , 4 , 14 , 7 , 5 , 11 ,
                10 , 15 , 4 , 2 , 7 , 12 , 9 , 5 , 6 , 1 , 13 , 14 , 0 , 11 , 3 , 8 ,
                9 , 14 , 15 , 5 , 2 , 8 , 12 , 3 , 7 , 0 , 4 , 10 , 1 , 13 , 11 , 6 ,
                4 , 3 , 2 , 12 , 9 , 5 , 15 , 10 , 11 , 14 , 1 , 7 , 6 , 0 , 8 , 13
        } ,
        {//S7盒子
                4 , 11 , 2 , 14 , 15 , 0 , 8 , 13 , 3 , 12 , 9 , 7 , 5 , 10 , 6 , 1 ,
                13 , 0 , 11 , 7 , 4 , 9 , 1 , 10 , 14 , 3 , 5 , 12 , 2 , 15 , 8 , 6 ,
                1 , 4 , 11 , 13 , 12 , 3 , 7 , 14 , 10 , 15 , 6 , 8 , 0 , 5 , 9 , 2 ,
                6 , 11 , 13 , 8 , 1 , 4 , 10 , 7 , 9 , 5 , 0 , 15 , 14 , 2 , 3 , 12
        } ,
        {//S8盒子
                13 , 2 , 8 , 4 , 6 , 15 , 11 , 1 , 10 , 9 , 3 , 14 , 5 , 0 , 12 , 7 ,
                1 , 15 , 13 , 8 , 10 , 3 , 7 , 4 , 12 , 5 , 6 , 11 , 0 , 14 , 9 , 2 ,
                7 , 11 , 4 , 1 , 9 , 12 , 14 , 2 , 0 , 6 , 10 , 13 , 15 , 3 , 5 , 8 ,
                2 , 1 , 14 , 7 , 4 , 10 , 8 , 13 , 15 , 12 , 9 , 0 , 3 , 5 , 6 , 11
        }
} ;

//P盒置换
const static unsigned char p_table[32] = {
        16 , 7 , 20 , 21 , 29 , 12 , 28 , 17 , 1 , 15 , 23 , 26 , 5 , 18 , 31 , 10 ,
        2 , 8 , 24 , 14 , 32 , 27 , 3 , 9 , 19 , 13 , 30 , 6 , 22 , 11 , 4 , 25
} ;

//末置换
const static unsigned char ipr_table[64] = {
        40 , 8 , 48 , 16 , 56 , 24 , 64 , 32 , 39 , 7 , 47 , 15 , 55 , 23 , 63 , 31 ,
        38 , 6 , 46 , 14 , 54 , 22 , 62 , 30 , 37 , 5 , 45 , 13 , 53 , 21 , 61 , 29 ,
        36 , 4 , 44 , 12 , 52 , 20 , 60 , 28 , 35 , 3 , 43 , 11 , 51 , 19 , 59 , 27 ,
        34 , 2 , 42 , 10 , 50 , 18 , 58 , 26 , 33 , 1 , 41 , 9 , 49 , 17 , 57 , 25
} ;

//将数据块初始置换为左右两个部分
int ip(const Block & block , HBlock & left , HBlock & right)
{
    for(size_t i = 0 ; i < right.size() ; ++i)
        right[i] = block[ip_table[i] - 1] ;//获取置换后的右半部分
    for(size_t i = 0 ; i < left.size() ; ++i)
        left[i] = block[ip_table[i + left.size()] - 1] ;//获取置换后的左半部分
    return 0 ;
}

//一轮加解密运算，不带交换
int des_turn(HBlock & left , HBlock & right , const Code & subkey)
{
    Code code ;//48位数据块
    HBlock pcode ;//32位数据块
    //将右半部分扩展为48位
    for(size_t i = 0 ; i < code.size() ; ++i)
        code[i] = right[expa_perm[i] - 1] ;//扩展置换
    code ^= subkey ;//与子密钥异或
    //S盒代替
    std::bitset<4> col ;//S盒的列
    std::bitset<2> row ;//S盒的行
    for(size_t i = 0 ; i < 8 ; ++i)
    {//8个盒子
        row[0] = code[6 * i] ;//获取行标
        row[1] = code[6 * i + 5] ;
        col[0] = code[6 * i + 1] ;//获取列标
        col[1] = code[6 * i + 2] ;
        col[2] = code[6 * i + 3] ;
        col[4] = code[6 * i + 4] ;
        std::bitset<4> temp(sbox[i][row.to_ulong() * 16 + col.to_ulong()]) ;
        for(size_t j = 0 ; j < temp.size() ; ++j)
            code[4 * i + j] = temp[j] ;//将32位暂存于48位中
    }
    for(size_t i = 0 ; i < pcode.size() ; ++i)
        pcode[i] = code[p_table[i] - 1] ;//P盒置换
    left ^= pcode ;//异或
    return 0 ;
}

//交换左右两个部分
int exchange(HBlock & left , HBlock & right)
{
    HBlock temp ;
    for(size_t i = 0 ; i < temp.size() ; ++i)
        temp[i] = left[i] ;
    for(size_t i = 0 ; i < left.size() ; ++i)
        left[i] = right[i] ;
    for(size_t i = 0 ; i < right.size() ; ++i)
        right[i] = temp[i] ;
    return 0 ;
}

//将左右两部分数据进行末置换形成一个数据块
int rip(const HBlock & left , const HBlock & right , Block & block)
{
    for(size_t i = 0 ; i < block.size() ; ++i)
    {
        if(ipr_table[i] <= 32)
            block[i] = right[ipr_table[i] - 1] ;//从right部分获取数据
        else
            block[i] = left[ipr_table[i] - 32 - 1] ;//从left部分获取数据
    }
    return 0 ;
}

//密钥置换表，将64位密钥置换压缩置换为56位
const static unsigned char key_table[56] = {
        57 , 49 , 41 , 33 , 25 , 17 , 9 , 1 ,
        58 , 50 , 42 , 34 , 26 , 18 , 10 , 2 ,
        59 , 51 , 43 , 35 , 27 , 19 , 11 , 3 ,
        60 , 52 , 44 , 36 , 63 , 55 , 47 , 39 ,
        31 , 23 , 15 , 7 , 62 , 54 , 46 , 38 ,
        30 , 22 , 14 , 6 , 61 , 53 , 45 , 37 ,
        29 , 21 , 13 , 5 , 28 , 20 , 12 , 4
} ;

//每轮移动的位数
const static unsigned char bit_shift[16] = {
        1 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 1
} ;

//压缩置换表，56位密钥压缩位48位密钥
const static unsigned char comp_perm[48] = {
        14 , 17 , 11 , 24 , 1 , 5 , 3 , 28 ,
        15 , 6 , 21 , 10 , 23 , 19 , 12 , 4 ,
        26 , 8 , 16 , 7 , 27 , 20 , 13 , 2 ,
        41 , 52 , 31 , 37 , 47 , 55 , 30 , 40 ,
        51 , 45 , 33 , 48 , 44 , 49 , 39 , 56 ,
        34 , 53 , 46 , 42 , 50 , 36 , 29 , 32
} ;

//获取bkey产生的第n轮子密钥
Code getkey(const unsigned int n , const Block & bkey)
{//n在区间[0,15]之间取值，bkey为64位密钥
    Code result ;//返回值,48位子密钥
    Key key ;//56位密钥
    unsigned int klen = key.size() , rlen = result.size() ;//分别为56和48
    //获取56位密钥
    for(size_t i = 0 ; i < key.size() ; ++i)
        key[i] = bkey[key_table[i] - 1] ;//密钥置换
    for(size_t i = 0 ; i <= n ; ++i)
    {//循环移位
        for(size_t j = 0 ; j < bit_shift[i] ; ++j)
        {
            //将密钥循环位暂存在result中
            result[rlen - bit_shift[i] + j] = key[klen - bit_shift[i] + j] ;
            result[rlen / 2 - bit_shift[i] + j] = key[klen / 2 - bit_shift[i] + j] ;
        }
        key <<= bit_shift[i] ;//移位
        for(size_t j = 0 ; j < bit_shift[i] ; ++j)
        {
            //写回key中
            key[klen / 2 + j] = result[rlen - bit_shift[i] + j] ;
            key[j] = result[rlen / 2 - bit_shift[i] + j] ;
        }
    }
    //压缩置换
    for(size_t i = 0 ; i < result.size() ; ++i)
        result[i] = key[comp_perm[i] - 1] ;
    return result ;
}

//加解密运算
int des(Block & block , Block & bkey , const Method method)
{//block为数据块，bkey为64位密钥
    HBlock left , right ;//左右部分
    ip(block , left , right) ;//初始置换
    switch(method)
    {
        case e://加密
            for(char i = 0 ; i < 16 ; ++i)
            {
                Code key = getkey(i , bkey) ;
                des_turn(left , right , key) ;
                if(i != 15) exchange(left , right) ;
            }
            break ;
        case d://解密
            for(char i = 15 ; i >= 0 ; --i)
            {
                Code key = getkey(i , bkey) ;
                des_turn(left , right , key) ;
                if(i != 0) exchange(left , right) ;
            }
            break ;
        default:
            break ;
    }
    rip(left , right , block) ;//末置换
    return 0 ;
}

